/**
 * Copyright (c) 2010
 * Bert Young. UESTC. 
 */

#ifndef BERT_SOCKETCONNECTION_H
#define BERT_SOCKETCONNECTION_H

#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include "Buffer.h"
#include "Noncopyable.h"
#include "Mutex.h"

/**
 * Abstraction for a TCP connection
 */
class SocketConnection : private Noncopyable
{
public:
	/**
	 * Constructor
	 */
	SocketConnection(int localfd = -1, const struct sockaddr_in * remote = NULL);
	/**
	 * Destructor
	 */
	~SocketConnection();
	/**
	 * Time out for poll
	 */
	static const int READ_TIMEOUT = 2000;
	static const int WRITE_TIMEOUT = 5000;
	/**
	 * The maximum size of a message
	 */
	static const unsigned int SIZE_MASK     = _64KB - 1;
	static const unsigned int MAX_DATASIZE  = SIZE_MASK;
	/**
	 * Header length, its content indicates the length of the message
	 */
	static const unsigned int HEAD = sizeof(unsigned int);
	/**
	 * Mark for socket kernel buffer unavailable
	 */
	static const int COMPLETE_READ = 0x01;
	static const int COMPLETE_WRITE= 0x01<<1;
	unsigned int bitmask;

	/**
	 * For human readability
	 */
	enum STATUS
	{
		TIMEOUT = 0,
		ERROR = -1,
		EOFSOCKET = -2,
	};
	/**
	 * The local socket and address
	 */
	int localSock;
	struct sockaddr_in localAddr;
	/**
	 * The remote end and address
	 */
	int remoteSock;
	struct sockaddr_in remoteAddr;
	/**
	 * The user buffer for recv and send data
	 */
	FlexibleBuffer recvBuf;
	FlexibleBuffer sendBuf;
	/**
	 * Mutex for sendBuf, because IO thread and logical thread will operate it
	 * For epoll fd, it is do by IO and verify threads;
	 */
	Mutex mutex;
	/**
	 * Poll for read or write
	 */
	int waitForRead(int waittime = READ_TIMEOUT );
	int waitForWrite(int waittime = WRITE_TIMEOUT );
	/**
	 * Pack and unpack data
	 */
	template <typename BUFFER>
	unsigned int packPacket(const void * data, BUFFER & dest, unsigned int size);
	unsigned int unpackPacket(const unsigned char * in, void * dest, unsigned int wholesize);
	/**
	 * Receive data
	 */
private:
	int recvFromKernel();
public:
	int recv();
	int recvPacket(void * out, bool wait = true);
	int recvPacketNoPoll(void * out);
	/**
	 * Send data
	 */
private:
	int sendToKernel(const void * data, unsigned int len);
	int send(const void * data, unsigned int len);
	bool sendToKernelFully(const void * data, unsigned int len);
public:
	int sendPacket(const void * data, unsigned int len, bool buffer = false);//called by timeticker
	bool sync( );// called by IO thread
	bool finalSync( );
	/**
	 * For epoll me
	 */
	void epollAdd(int epfd, __uint32_t events, void * ptr )
	{
		mutex.lock(); 
		struct epoll_event event;
		event.events = events;
		event.data.ptr = ptr;
		if( 0 != epoll_ctl(epfd, EPOLL_CTL_ADD, this->localSock, &event ) )
			assert(false && "epoll ctl add error");
		mutex.unlock();
	}
	/**
	 * For not epoll me
	 */
	int epollDel( int epfd )
	{
		struct epoll_event dummy;
		mutex.lock();
		if( 0 != epoll_ctl(epfd, EPOLL_CTL_DEL, this->localSock, &dummy))
			assert(false && "epoll ctl del error");
		mutex.unlock();
	}
};

#endif

